package com.coin900.coin.rabbitmq.receiver;

import com.coin900.coin.base.BxConstants;
import com.coin900.coin.base.CoinConstants;
import com.coin900.coin.base.restTemplate.BasicAuthRestTemplate;
import com.coin900.coin.base.utils.CoinUserWalletUtils;
import com.coin900.coin.persist.fu.FuCapitalTransactionVO;
import com.coin900.coin.persist.fu.FuUserWalletVO;
import com.coin900.coin.rabbitmq.json.Details;
import com.coin900.coin.rabbitmq.json.TransactionVO;
import com.coin900.coin.service.fu.IFuCapitalDetailService;
import com.coin900.coin.service.fu.IFuCapitalTransactionService;
import com.coin900.coin.service.fu.IFuUserWalletService;
import com.google.gson.Gson;
import com.value.exception.BusinessException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 充值提现定时任务
 * <p>
 * 每 30s 查询一次 fu_capital_transaction 表
 * 检查其中未确认的充值记录进行确认
 * 确认通过调用 `/gettransaction?currency=xxx&txid=xxx`
 * 其中 currency 为 eth
 * <p>
 * is_success 默认为 2 表示正在确认中
 * 成功标记为 1
 * 失败标记为 0
 * <p>
 * PS:
 * 记录产生的时间超过 1h 则表示充值是失败的
 *
 * @author shui
 * @create 2018-02-02
 **/
@Component
public class CapitalTask {

    private static Long ONE_HOUR = 3600000L;

    private static final Log logger = LogFactory.getLog(CoinUserWalletUtils.class);

    /**
     * 充值
     *
     * 6s 扫描一次
     */
    @Scheduled(fixedDelay = 6000)
    public void recharge() {
        this.confirmCapital(1);
    }

    /**
     * 提现
     *
     * 6s 扫描一次
     */
    @Scheduled(fixedDelay = 6000)
    public void withdraw() {
        this.confirmCapital(2);
    }

    /**
     * 获取待确认的充值提现信息
     *
     * @param type 1充值 2提现
     * @return
     */
    private void confirmCapital(int type) {
        Map<String, Object> params = new HashMap<>();
        params.put("type", type);
        params.put("isSuccess", 2);
        params.put("dataStatus", 1);
        List<FuCapitalTransactionVO> fuCapitalTransactionVOS = this.transactionService.loadFuCapitalTransaction(params);
        if (!fuCapitalTransactionVOS.isEmpty()) {
            for (FuCapitalTransactionVO capitalTransactionVO : fuCapitalTransactionVOS) {
                String txid = capitalTransactionVO.getTxid();
                TransactionVO jsonTransaction = this.getTransaction(txid);
                if (jsonTransaction.getSuccess()) {
                    // 确认成功
                    this.initCapitalTransaction(jsonTransaction, capitalTransactionVO);
                    this.confirmSuccess(capitalTransactionVO);
                } else {
                    // 确认失败
                    this.confirmFail(capitalTransactionVO);
                }
            }
        }
    }

    /**
     * 确认成功时的处理
     *
     * @param capitalTransactionVO
     */
    private void confirmSuccess(FuCapitalTransactionVO capitalTransactionVO) {
        Integer type = capitalTransactionVO.getType();
        capitalTransactionVO.setIsSuccess(CoinConstants.COIN_CAPITAL_CONFIRM_SUCCESS);
        capitalTransactionVO.setModifiedDate(new Date());
        String value = capitalTransactionVO.getValue();
        Double verify = this.value2Verify(value);
        capitalTransactionVO.setVerify(verify);
        if (new Integer(1).equals(type)) {
            // 充值成功
            String toAddress = capitalTransactionVO.getToAddress();
            // 根据充值地址查询用户钱包，钱包金额变化
            Map<String, Object> params = new HashMap<>();
            params.put("address", toAddress);
            params.put("dataStatus", 1);
            List<FuUserWalletVO> fuUserWalletVOS = this.walletService.loadFuUserWallet(params);
            if (fuUserWalletVOS.isEmpty()) {
                logger.error("没有用户的充值地址为：" + toAddress);
            }

            this.detailService.doRechargeConfirmSuccess(capitalTransactionVO);
            this.brain(toAddress);
        } else {
            // 提现成功
            this.detailService.doWithdrawConfirmSuccess(capitalTransactionVO);
        }
    }

    /**
     * 确认失败时的处理
     * <p>
     * 创建时间已经超过 1 小时则最终标记为失败
     * 未超过一小时不做处理，等待下一次请求
     *
     * @param transactionVO
     */
    private void confirmFail(FuCapitalTransactionVO transactionVO) {
        Date createdDate = transactionVO.getCreatedDate();
        Date now = new Date();
        if (now.getTime() - createdDate.getTime() > CapitalTask.ONE_HOUR) {
            transactionVO.setModifiedDate(new Date());
            transactionVO.setIsSuccess(CoinConstants.COIN_CAPITAL_CONFIRM_FAIL);
            this.transactionService.updateFuCapitalTransaction(transactionVO);
        }
    }

    /**
     * ===================================================================================
     * <p>
     * 调用接口：/gettransaction?currency=xxx&txid=xxx
     * 获取某笔交易(提现)的详情
     * 用来判断转帐是否成
     * <p>
     * {isSuccess: true/false, details: {confirmations: 23, from:, to, value:}
     * <p>
     * PS:
     * value 值使用对应币种的最小单位
     * currency 目前只能使用 eth
     * <p>
     * ===================================================================================
     *
     * @param txid 待确认记录的 txid
     * @return 接口返回的确认信息
     */
    private TransactionVO getTransaction(String txid) {
        String currency = "etc";
        String url = BxConstants.BX_BLOCK_CHAIN_GET_TRANSACTION + "?currency=" + currency + "&txid=" + txid;
        BasicAuthRestTemplate restTemplate = new BasicAuthRestTemplate(BxConstants.BX_BLOCK_CHAIN_BASIC_NAME, BxConstants.BX_BLOCK_CHAIN_BASIC_PASS);
        ResponseEntity<String> result = restTemplate.getForEntity(url, String.class);
        String body = result.getBody();
        TransactionVO apiTransaction = new Gson().fromJson(body, TransactionVO.class);
        return apiTransaction;
    }

    /**
     * ETH 充值到账以后, 需要你这边调用一下 /drain 接口, 把用户钱包的钱汇入到热钱包 (BTC 不需要这步操作)
     * 由于目前之后 btc 充值，全部当成 eth 处理
     *
     * @param address
     * @return
     */
    private void brain(String address) {
        String currency = "etc";
        String url = BxConstants.BX_BLOCK_CHAIN_GET_DRAIN + "?currency=" + currency + "&address=" + address;
        BasicAuthRestTemplate restTemplate = new BasicAuthRestTemplate(BxConstants.BX_BLOCK_CHAIN_BASIC_NAME, BxConstants.BX_BLOCK_CHAIN_BASIC_PASS);
        restTemplate.getForEntity(url, String.class);
    }

    /**
     * eth 单位转成 double
     * <p>
     * value 的单位是该币种的最小单位，现在只支持 eth
     *
     * @param value
     * @return
     */
    private Double value2Verify(String value) {
        BigDecimal bigDecimal = new BigDecimal(value);
        /***** 现在是 eth 都按照是 18 位，以后有变化就改 *****/
        bigDecimal = bigDecimal.movePointLeft(18);
        Double verify = bigDecimal.doubleValue();
        return verify;
    }

    /**
     * 初始化 FuCapitalTransactionVO
     * 充值提现校验成功，将 json 数据转换为 FuCapitalTransactionVO 数据
     *
     * @param jsonTransaction      json 数据
     * @param capitalTransactionVO
     */
    private void initCapitalTransaction(TransactionVO jsonTransaction, FuCapitalTransactionVO capitalTransactionVO) {
        Details details = jsonTransaction.getDetails();
        Integer confirmations = details.getConfirmations();
        String from = details.getFrom();
        String to = details.getTo();
        String value = details.getValue();
        if (confirmations != null) {
            capitalTransactionVO.setConfirmations(confirmations);
        }
        if (from != null) {
            capitalTransactionVO.setFromAddress(from);
        }
        if (to != null) {
            capitalTransactionVO.setToAddress(to);
        }
        if (value != null) {
            capitalTransactionVO.setValue(value);
        }
        capitalTransactionVO.setIsSuccess(CoinConstants.COIN_CAPITAL_CONFIRM_SUCCESS);
    }

    @Autowired
    private IFuCapitalTransactionService transactionService;
    @Autowired
    private IFuCapitalDetailService detailService;
    @Autowired
    private IFuUserWalletService walletService;
}
